﻿////////////////////////////////////////////////////////////////////////////
//
//  Immense Engine Source File.
//  Copyright (C), Indloon 2012
// -------------------------------------------------------------------------
//  File name:   ieMathVector3.h
//  Description: ieMath vector 3D
//  Created:     8/12/2012 Genert Org
//  Compilers:   Visual C++ 2010 Express
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////
#ifndef __IMMENSEENGINE_MATH_VECTOR3D_H
#define __IMMENSEENGINE_MATH_VECTOR3D_H
#pragma once

/*  1. C L A S S
	=========================================================================== */

class ieVec3 {
public:
    float x;
	float y;
	float z;
                    ieVec3( void ); // type init
    explicit        ieVec3( const float value );
    explicit        ieVec3( const float x, const float y, const float z );
	
    float			operator [] ( const int index ) const;
    float &			operator [] ( const int index );
    ieVec3 &        operator = ( const ieVec3 &vec );
    bool            operator == ( const ieVec3 &vec ) const;
    bool            operator != ( const ieVec3 &vec ) const;
    ieVec3          operator - () const;
    ieVec3          operator + ( const ieVec3 &vec ) const;
    ieVec3          operator - ( const ieVec3 &vec ) const;
    ieVec3          operator * ( const ieVec3 &vec ) const;
    ieVec3          operator / ( const ieVec3 &vec ) const;
    ieVec3          operator + ( const float value ) const;
    ieVec3          operator - ( const float value ) const;
    ieVec3          operator * ( const float value ) const;
    ieVec3          operator / ( const float value ) const;
    ieVec3 &		operator += ( const ieVec3 &vec );
    ieVec3 &		operator -= ( const ieVec3 &vec );
    ieVec3 &		operator *= ( const ieVec3 &vec );
    ieVec3 &		operator /= ( const ieVec3 &vec );
    ieVec3 &		operator += ( const float value );
    ieVec3 &		operator -= ( const float value );
    ieVec3 &		operator *= ( const float value );
    ieVec3 &		operator /= ( const float value );
	bool            operator < ( const ieVec3 &vec ) const;
	bool            operator > ( const ieVec3 &vec ) const;
	
	void            Set( const float value );
    void            Set( const float x, const float y, const float z );
    void            Clear( void );
    void            Zero( void );
	bool            IsCleared( void ) const;
	bool            IsZero( void ) const;
	
	bool            AreEqual( const ieVec3 &vec ) const;
	bool            AreEqual( const ieVec3 &vec, float epsilon ) const;
	float           Length( void ) const;
	float			LengthFast( void ) const;
	float			LengthSqr( void ) const;
	float           Normalize( void );
	void            NormalizeFast( void );
	void			Truncate( void );
	void			Clamp( const ieVec3 &min, const ieVec3 &max );
	void			Clamp( const float min, const float max );
	void			Average(const ieVec3 &vec1, const ieVec3 &vec2 );
	ieVec3          Cross( const ieVec3 &a ) const;
	ieVec3 &        Cross( const ieVec3 &a, const ieVec3 &b );
	void            Snap( void );
	void			Lerp( const ieVec3 &from, const ieVec3 &to, const float frac );
	void			LerpFast( const ieVec3 &from, const ieVec3 &to, const float frac );
	void			SLerp( const ieVec3 &from, const ieVec3 &to, const float frac );
	void			SLerpFast( const ieVec3 &from, const ieVec3 &to, const float frac );
	ieVec3			Reflect( const ieVec3 &normal ) const;
	void			Reflect( const ieVec3 &vec, const ieVec3 &normal );
	ieVec3			ProjectOntoPlane( const ieVec3 &normal, const float overBounce );
	void			ProjectOntoPlane( const ieVec3 &vec, const ieVec3 &normal, const float overBounce );
	float			Distance( const ieVec3 &vec ) const;
	float			DistanceFast( const ieVec3 &vec ) const;
	float			DistanceSqr( const ieVec3 &vec ) const;

	ieVec2 &        ToVec2( void );
};


/*  2. F U N C T I O N S
	=========================================================================== */
	
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3::ieVec3( void ) 
{ 
    // ...
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3::ieVec3( const float value ) 
{
    this->x = value;
	this->y = value;
	this->z = value;
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3::ieVec3( const float x, const float y, const float z )
{
    this->x = x;
	this->y = y;
	this->z = z;
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec3::operator [] ( const int index ) const
{
    return ( &x )[index];
}
// -------------------------------------------------------------------------------------------------------------
ILINE float &ieVec3::operator [] ( const int index )
{
	return ( &x )[index];
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 &ieVec3::operator = ( const ieVec3 &vec )
{
	x = vec.x;
	y = vec.y;
	z = vec.z;

	return *this;
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec3::operator == ( const ieVec3 &vec ) const 
{
	if( x != vec.x || y != vec.y || z != vec.z )
	{
        return false;
	} 
	else 
	{
        return true;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec3::operator != ( const ieVec3 &vec ) const 
{
	if( x == vec.x && y == vec.y && z == vec.z )
	{
        return false;
	} 
	else 
	{
        return true;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::operator - () const 
{
	return ieVec3( -x, -y, -z );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::operator + ( const ieVec3 &vec ) const 
{
	return ieVec3( x + vec.x, y + vec.y, z + vec.z );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::operator - ( const ieVec3 &vec ) const 
{
	return ieVec3( x - vec.x, y - vec.y, z - vec.z );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::operator * ( const ieVec3 &vec ) const 
{
	return ieVec3( x * vec.x, y * vec.y, z * vec.z );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::operator / ( const ieVec3 &vec ) const 
{
	return ieVec3( x / vec.x, y / vec.y, z / vec.z );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::operator + ( const float value ) const 
{
	return ieVec3( x + value, y + value, z + value );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::operator - ( const float value ) const 
{
	return ieVec3( x - value, y - value, z - value );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::operator * ( const float value ) const 
{
	return ieVec3( x * value, y * value, z * value );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::operator / ( const float value ) const 
{
	float	scale = 1.0f / value;

	return ieVec3( x * scale, y * scale, z * scale );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 &ieVec3::operator += ( const float value )
{
	x += value;
	y += value;
	z += value;

	return *this;
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 &ieVec3::operator -= ( const float value )
{
	x -= value;
	y -= value;
	z -= value;

	return *this;
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 &ieVec3::operator *= ( const float value )
{
	x *= value;
	y *= value;
	z *= value;

	return *this;
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 &ieVec3::operator /= ( const float value )
{
	float	scale = 1.0f / value;

	x *= scale;
	y *= scale;
	z *= scale;

	return *this;
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec3::operator < ( const ieVec3 &vec ) const
{
    if( ( ( x < vec.x ) && ( y < vec.y ) && ( z < vec.z ) ) || ( ( x < vec.x ) || ( y < vec.y ) || ( z < vec.z ) ) )
	{
	    return true;
	}
	else if( ( ( x > vec.x ) && ( y > vec.y ) && ( z > vec.z ) ) || ( ( x > vec.x ) || ( y > vec.y ) || ( z > vec.z ) ) )
	{
	    return false;
	} 
	else
	{
	    return false;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec3::operator > ( const ieVec3 &vec ) const
{
    if( ( ( x > vec.x ) && ( y > vec.y ) && ( z > vec.z ) ) || ( ( x > vec.x ) || ( y > vec.y ) || ( z > vec.z ) ) )
	{
	    return true;
	}
	else if( ( ( x < vec.x ) && ( y < vec.y ) && ( z < vec.z ) ) || ( ( x < vec.x ) || ( y < vec.y ) || ( z < vec.z ) ) )
	{
	    return false;
	}
	else
	{
	    return false;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::Set( const float value )
{
    this->x = value;
    this->y = value;
    this->z = value;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::Set( const float x, const float y, const float z )
{
    this->x = x;
    this->y = y;
    this->z = z;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::Clear( void )
{
    x = y = z = 0.0f;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::Zero( void )
{
    x = y = z = 0.0f;
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec3::IsCleared( void ) const
{
    if( ( x != 0.0f ) ||
	    ( y != 0.0f ) ||
		( z != 0.0f ) )
	{
	    return false;
	} 
	else
	{
	    return true;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec3::IsZero( void ) const
{
    if( ( x != 0.0f ) ||
	    ( y != 0.0f ) ||
		( z != 0.0f ) )
	{
	    return false;
	} 
	else
	{
	    return true;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec3::AreEqual( const ieVec3 &vec ) const
{
    if( ( x != vec.x ) && ( y != vec.y ) && ( z != vec.z ) )
	{
	    return false;
	} 
	else
	{
	    return true;
	}
}
// -------------------------------------------------------------------------------------------------------------
ILINE bool ieVec3::AreEqual( const ieVec3 &vec, float epsilon ) const
{
    return ( ( ieMath::Fabs( x - vec.x ) < epsilon ) && ( ieMath::Fabs( y - vec.y ) < epsilon ) && ( ieMath::Fabs( z - vec.z ) < epsilon ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec3::Length( void ) const
{
    float length;

	length =  ( x * x ) + ( y * y ) + ( z * z );
	
	return ( (float) ieMath::Sqrt( length ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec3::LengthFast( void ) const 
{
	return ieMath::SqrtFast( ( x * x ) + ( y * y ) + ( z * z ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec3::LengthSqr( void ) const 
{
	return ( ( x * x ) + ( y * y ) + ( z * z ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec3::Normalize( void )
{
	float sqrLength, invLength;

	sqrLength = x * x + y * y + z * z;
	invLength = ieMath::Sqrt( sqrLength );
	x *= invLength;
	y *= invLength;
	z *= invLength;
	
	return ( invLength * sqrLength );
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::NormalizeFast( void )
{
	float	invLength;

	invLength = ieMath::RSqrt( ( x * x ) + ( y * y ) + ( z * z ) );

	x *= invLength;
	y *= invLength;
	z *= invLength;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::Truncate( void )
{
	x = ieMath::FloatToInt( x );
	y = ieMath::FloatToInt( y );
	z = ieMath::FloatToInt( z );
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::Clamp( const ieVec3 &min, const ieVec3 &max )
{
	x = ( x < min.x ) ? min.x : ( x > max.x ) ? max.x : x;
	y = ( y < min.y ) ? min.y : ( y > max.y ) ? max.y : y;
	z = ( z < min.z ) ? min.z : ( z > max.z ) ? max.z : z;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::Clamp( const float min, const float max )
{
	x = ( x < min ) ? min : ( x > max ) ? max : x;
	y = ( y < min ) ? min : ( y > max ) ? max : y;
	z = ( z < min ) ? min : ( z > max ) ? max : z;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::Average( const ieVec3 &vec1, const ieVec3 &vec2 )
{
	x = ( vec1.x + vec2.x ) * 0.5f;
	y = ( vec1.y + vec2.y ) * 0.5f;
	z = ( vec1.z + vec2.z ) * 0.5f;
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::Cross( const ieVec3 &a ) const
{
	return ieVec3( y * a.z - z * a.y,
                 z * a.x - x * a.z, 
				 x * a.y - y * a.x );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 &ieVec3::Cross( const ieVec3 &a, const ieVec3 &b ) 
{
	x = a.y * b.z - a.z * b.y;
	y = a.z * b.x - a.x * b.z;
	z = a.x * b.y - a.y * b.x;

	return *this;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::Snap( void )
{
    x = ieMath::Round( x );
	y = ieMath::Round( y );
	z = ieMath::Round( z );
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::Lerp( const ieVec3 &from, const ieVec3 &to, const float frac )
{
	if ( frac <= 0.0f )
	{
		*this = from;
		return;
	}

	if ( frac >= 1.0f )
	{
		*this = to;
		return;
	}

	x = from.x + ( to.x - from.x ) * frac;
	y = from.y + ( to.y - from.y ) * frac;
	z = from.z + ( to.z - from.z ) * frac;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::LerpFast( const ieVec3 &from, const ieVec3 &to, const float frac )
{
	x = from.x + ( to.x - from.x ) * frac;
	y = from.y + ( to.y - from.y ) * frac;
	z = from.z + ( to.z - from.z ) * frac;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::SLerp( const ieVec3 &from, const ieVec3 &to, const float frac )
{
	float	omega, sinom, cosom;
	float	fScale, tScale;

	if( frac <= 0.0f )
	{
		*this = from;
		return;
	}

	if( frac >= 1.0f )
	{
		*this = to;
		return;
	}

	cosom = from.x * to.x + from.y * to.y + from.z * to.z;

	if ( ( 1.0f - cosom ) > 0.0f )
	{
		omega = ieMath::ACos( cosom );
		sinom = 1.0f / ieMath::Sin( omega );

		fScale = ieMath::Sin( ( 1.0f - frac ) * omega ) * sinom;
		tScale = ieMath::Sin( frac * omega ) * sinom;
	}
	else 
	{
		fScale = 1.0f - frac;
		tScale = frac;
	}

	x = from.x * fScale + to.x * tScale;
	y = from.y * fScale + to.y * tScale;
	z = from.z * fScale + to.z * tScale;
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::SLerpFast( const ieVec3 &from, const ieVec3 &to, const float frac )
{
	float	omega, sinom, cosom;
	float	fScale, tScale;

	cosom = from.x * to.x + from.y * to.y + from.z * to.z;

	if ( ( 1.0f - cosom ) > 0.0f )
	{
		omega = ieMath::ACos( cosom );
		sinom = 1.0f / ieMath::Sin( omega );

		fScale = ieMath::Sin( ( 1.0f - frac ) * omega ) * sinom;
		tScale = ieMath::Sin( frac * omega ) * sinom;
	}
	else 
	{
		fScale = 1.0f - frac;
		tScale = frac;
	}

	x = from.x * fScale + to.x * tScale;
	y = from.y * fScale + to.y * tScale;
	z = from.z * fScale + to.z * tScale;
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::Reflect( const ieVec3 &normal ) const 
{
	float	d;

	d = 2.0f * ( x * normal.x + y * normal.y + z * normal.z );

	return ieVec3( x - normal.x * d, y - normal.y * d, z - normal.z * d );
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::Reflect( const ieVec3 &vec, const ieVec3 &normal )
{
	float	d;

	d = 2.0f * ( vec.x * normal.x + vec.y * normal.y + vec.z * normal.z );

	x = vec.x - normal.x * d;
	y = vec.y - normal.y * d;
	z = vec.z - normal.z * d;
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec3 ieVec3::ProjectOntoPlane( const ieVec3 &normal, const float overBounce )
{
	float	backOff;

	backOff = x * normal.x + y * normal.y + z * normal.z;

	if( backOff < 0.0f )
	{
		backOff *= overBounce;
	}
	else
	{
		backOff /= overBounce;
	}

	return ieVec3( x - normal.x * backOff, y - normal.y * backOff, z - normal.z * backOff );
}
// -------------------------------------------------------------------------------------------------------------
ILINE void ieVec3::ProjectOntoPlane( const ieVec3 &vec, const ieVec3 &normal, const float overBounce )
{
	float	backOff;

	backOff = vec.x * normal.x + vec.y * normal.y + vec.z * normal.z;

	if ( backOff < 0.0f )
	{
		backOff *= overBounce;
	}
	else
	{
		backOff /= overBounce;
	}

	x = vec.x - normal.x * backOff;
	y = vec.y - normal.y * backOff;
	z = vec.z - normal.z * backOff;
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec3::Distance( const ieVec3 &vec ) const 
{
	float	xd, yd, zd;

	xd = vec.x - x;
	yd = vec.y - y;
	zd = vec.z - z;

	return ieMath::Sqrt( ( xd * xd ) + ( yd * yd ) + ( zd * zd ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec3::DistanceFast( const ieVec3 &vec ) const 
{
	float	xd, yd, zd;

	xd = vec.x - x;
	yd = vec.y - y;
	zd = vec.z - z;

	return ieMath::SqrtFast( ( xd * xd ) + ( yd * yd ) + ( zd * zd ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE float ieVec3::DistanceSqr( const ieVec3 &vec ) const
{
	float	xd, yd, zd;

	xd = vec.x - x;
	yd = vec.y - y;
	zd = vec.z - z;

	return ( ( xd * xd ) + ( yd * yd ) + ( zd * zd ) );
}
// -------------------------------------------------------------------------------------------------------------
ILINE ieVec2 &ieVec3::ToVec2( void )
{
	return ( *reinterpret_cast <ieVec2*>( this ) );
}
// -------------------------------------------------------------------------------------------------------------


#endif // __IMMENSEENGINE_MATH_VECTOR3D_H